/*
 * store_recover.c
 *
 *  Created on: 2015年1月26日
 *      Author: chens
 *      1、该模块的功能是将磁盘作为缓冲，将网络数据写入文件缓存，并按指定频率重新加载
 *      2、通过 "读写序号" 来确认当前读或者写所应该操作的缓存文件（根据 "读写序号" 确定文件名）
 *      3、缓存文件命名规则： 如果读序号(r_index)为 0x12345678， 则此时应将数据写入文件：
 *      	ROOT/0x12的十进制值/0x34的十进制值/0x56的十进制值/序号的十进制值.data
 *      	实际是: ROOT/18/52/86/305419896.data
 *      4、序号是递增的，递增到unsinged int所能表达的最大值后回到0，约42亿多
 *
 *      5、适用场景：读操作比写操作慢，在一段时间内用内存缓存可能会溢出
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <cm/file/cm_file.h>

#define RECORD_PATH "/home/ftp/record_data/"
#define RECORD_INDEX_FILE "/home/ftp/record_data/record_index.index"

#define MAGIC_NUMBER	0x20150126

struct record_index {
	unsigned int magic_number;
	unsigned int r_index;
	unsigned int w_index;
};

static struct record_index * record_index = NULL;
static pthread_mutex_t record_lock = PTHREAD_MUTEX_INITIALIZER;

/*
 * @brief	将读写序号存入文件保存
 */
static inline int save_record_index(void)
{
	FILE* fp = fopen(RECORD_INDEX_FILE, "w");

	if (fp == NULL) {
		printf("\t Error open(w) index file(%s)\n", RECORD_INDEX_FILE);
		return -1;
	}
	fwrite(record_index, sizeof(struct record_index), 1, fp);
	fclose(fp);
	return 0;
}

/*
 * @brief	从文件中加载保存的读写序号
 */
static inline int load_record_index()
{
	FILE* fp = fopen(RECORD_INDEX_FILE, "r");
	if (fp == NULL) {
		printf("\t Error open(r) index file(%s)\n", RECORD_INDEX_FILE);
		return -1;
	}

	if (fread(record_index, sizeof(struct record_index), 1, fp) != 1) {
		fclose(fp);
		printf("\t Read record index from file failed.\n");
		return -1;
	}
	fclose(fp);
	if (record_index->magic_number != MAGIC_NUMBER) {
		printf("\t Error magic number.\n");
		return -1;
	} else {
		return 0;
	}
}

static inline int reset_record_index()
{
	record_index->magic_number = MAGIC_NUMBER;
	record_index->r_index = 0;
	record_index->w_index = 0;
	return (save_record_index());
}

static void * store_recover_thread(void *arg);
/*
 * @brief	初始化模块,成功返回0 失败返回-1
 */
int store_recover_init(void *m)
{
	printf("\t\t store_recover_init\n");
	pthread_t tid = -1;
	if (record_index == NULL)
		record_index = (struct record_index *) malloc(
				sizeof(struct record_index));
	if (record_index == NULL)
		goto _FAILED;

	if (cm_file_exist(RECORD_INDEX_FILE)) {
		if (0 != load_record_index()) {
			if (0 != reset_record_index())
				goto _FAILED;
		}
	} else {
		create_multi_dir(RECORD_PATH);
		if (0 != reset_record_index())
			goto _FAILED;
	}
	pthread_create(&tid, NULL, store_recover_thread, m);
	printf("\t\t store_recover_init success.\n");
	return 0;

	_FAILED: ;
	printf("\t\t store_recover_init failed.\n");
	if (record_index != NULL)
		free(record_index);
	return -1;
}

int store_recover_put(const char * data, int size)
{
	int ret = -1;
	char path[256];	// PATH/"255/255/255/255/"
	char filename[256];

	pthread_mutex_lock(&record_lock);

	unsigned int i = record_index->w_index;
	if (i == (record_index->r_index - 1)) { // 已满，不能再写入（实际当前位置可以写入但是序号不能递增）
		pthread_mutex_unlock(&record_lock);
		return -1;
	}

	sprintf(path, "%s/%d/%d/%d/", RECORD_PATH, i >> 24,
			(i & 0x00FF0000) >> 16, (i & 0x0000FF00) >> 8);
	create_multi_dir(path);
	sprintf(filename, "%s%u.data", path, i);

	FILE* fp = NULL;
	fp = fopen(filename, "wb");
	if (fp != NULL) {
		int w1 = fwrite(&size, sizeof(int), 1, fp);
		int w2 = fwrite(data, size, 1, fp);
		fclose(fp);
		if (w1 == 1 && w2 == 1) { // 全部都写入成功才会递增
			record_index->w_index++;
			ret = 0;
			save_record_index();
		} else {
			printf("\t Error write record file.\n");
		}
	}

	printf("\tP : r_index = %u, w_index = %u\n", record_index->r_index,
			record_index->w_index);
	printf("\t\t store_recover_put (%s) (%d)\n", filename, ret);
	pthread_mutex_unlock(&record_lock);

	return ret;
}

static inline void _remove_file_path(unsigned int idx)
{
	char name[256];
	unsigned char l1, l2, l3, l4;
	l1 = (unsigned char) (idx >> 24);
	l2 = (unsigned char) ((idx & 0x00FF0000) >> 16);
	l3 = (unsigned char) ((idx & 0x0000FF00) >> 8);
	l4 = (unsigned char) (idx & 0xFF);

	sprintf(name, "%s/%d/%d/%d/%u.data", RECORD_PATH, l1, l2, l3, idx);
	remove(name);

	if (l4 == 0xFF) { //最后一个文件被删除则删除目录
		sprintf(name, "%s/%d/%d/%d", RECORD_PATH, l1, l2, l3);
		rmdir(name);
	} else {
		return;
	}

	if (l3 == 0xFF) {
		sprintf(name, "%s/%d/%d", RECORD_PATH, l1, l2);
		rmdir(name);
	} else {
		return;
	}

	if (l2 == 0xFF) {
		sprintf(name, "%s/%d", RECORD_PATH, l1);
		rmdir(name);
	}
}

int store_recover_get(char **data, int *size)
{
	int ret = -1;
	char path[256];	// PATH/"255/255/255/255/"
	char filename[256];

	pthread_mutex_lock(&record_lock);

	unsigned int i = record_index->r_index;
	if (i == record_index->w_index) {	// 空的，无数据,等待数据写入
		pthread_mutex_unlock(&record_lock);
		return -1;
	}

	sprintf(path, "%s/%d/%d/%d/", RECORD_PATH, i >> 24,
			(i & 0x00FF0000) >> 16, (i & 0x0000FF00) >> 8);
	sprintf(filename, "%s%u.data", path, i);

	FILE* fp = NULL;
	fp = fopen(filename, "r");
	if (fp != NULL) {
		int w1 = fread(size, sizeof(int), 1, fp);
		if (w1 == 1) {
			(*data) = (char *) malloc(*size);
			if (*data != NULL) {
				int w2 = fread(*data, *size, 1, fp);
				if (w2 == 1) {
					record_index->r_index++;
					ret = 0;
					save_record_index();
				} else {
					free(*data);
					*data = NULL;
					printf("\tRead data failed.\n");
				}
			}
		} else {
			printf("\tRead size failed.\n");
		}
		fclose(fp);
	}
	printf("\tG : r_index = %u, w_index = %u\n", record_index->r_index,
			record_index->w_index);
	printf("\t\t store_recover_get (%s) (%d)\n", filename, ret);

	_remove_file_path(i);

	pthread_mutex_unlock(&record_lock);

	return ret;
}

static void * store_recover_thread(void *arg)
{
	while (1) {
		char *data = NULL;
		int size = 0;
		// store_recover_get 这个函数在内部malloc了内存用来存放数据， 用完要释放
		if (0 == store_recover_get(&data, &size)) {
			free(data);
			data = NULL;
		}
		sleep(10);
		// TODO 如果磁盘不够则删掉一些数据, 从r_index处
	}

	return NULL;
}

